Boto3でS3にオブジェクトをGet・Putする方法まとめ
簡単まとめ
- Binary形式でデータはやり取りする
- BytesIOを使えばオンメモリで使える
Boto3のS3オブジェクトの読み書き
AWS SDKのPython版であるBoto3ではいくつかの方法でオブジェクトのアップロード・ダウンロードが可能です。 大きく分けて次の2つの方式があります。
- ファイルの様な操作(
cp
コマンドに近い感じ) - HTTPのような操作
ファイルのような操作は次のように書きます。
bucket.upload_file('/local/path/to/target', 'object/key') bucket.download_file('object/key', '/local/path/to/target')
ファイル操作のcp
コマンドのように引数が[SRC] [DIST]
の順になっています。
この方式のAPIを便宜的にファイル版と呼びます。
HTTPのような操作の場合は次のように書きます。
bucket.Object('object/key').get()['Body'] bucket.Object('object/key').put(Body=b'Lorem ipsum dolor sit amet')
HTTPクライアントのようにオブジェクトはBody
を通してやり取りします。
この方式のAPIを便宜的にHTTP版と呼びます。(ファイル版でもHTTP経由で通信します)
以下ではファイル版とHTTP版についてそれぞれ使い方を見ていきます。
準備
以下では次のようなパッケージがインポートされ、変数が定義されているとして話を進めます。
from pathlib import Path import boto3 import io s3 = boto3.resource('s3') bucket = s3.Bucket('boto-io') KEY = 'ipsum.txt' ORIGIN_PATH = Path('ipsum.txt') SAVE_PATH = Path('saved.txt')
ファイル版
ファイル版ではディスクを使用する場合とオンメモリで処理する場合の2パターンを説明します。
直接ディスクから読み込む
ファイルのパスを指定してアップロードする方法です。
bucket.upload_file(str(ORIGIN_PATH), KEY) bucket.download_file(KEY, str(SAVE_PATH))
file objectを使用する
open
関数を利用して、ファイルを読み書き可能です。
注意点はバイナリモードでファイルを開くことです。
バイナリのfile object
しか受け付けないようです。
with ORIGIN_PATH.open('rb') as f: bucket.upload_fileobj(f, KEY)
以下はダメな例です。md5ハッシュが計算できないとのエラーが発生します。
with PATH.open('r') as f: bucket.upload_fileobj(f, KEY)
Pythonではb
オプションをつけてファイルを開くとテキストではなくバイナリとして読み込まれます。
以下はモードによるfile object
の違いです。
BufferedReader
はバイナリのfile object
です。
with ORIGIN_PATH.open('r') as f: print(f'mode=r: {type(f)}') # -> "mode=r: <class '_io.TextIOWrapper'>" with ORIGIN_PATH.open('rb') as f: print(f'mode=rb: {type(f)}') # -> "mode=rb: <class '_io.BufferedReader'>"
BytesIOを使用してオンメモリで処理する
バイナリのfile object
であれば良いので、適切な物を使えばオンメモリでファイルのやりとりが可能です。
以下ではBytesIOを使用してファイルのやりとりをしています。
with SAVE_PATH.open('wb') as f: bucket.download_fileobj(KEY, f) with io.BytesIO(b'Lorem ipsum dolor sit amet') as f: bucket.upload_fileobj(f, KEY)
read
で読み出す場合は注意が必要です。書き込んだあとはオフセットが進んでいるので0
に戻す必要があります。
getvalues
を使用した場合は不要です。
with io.BytesIO() as f: print(f.read()) # -> b'' f.seek(0) print(f.read()) # -> b'Lorem ipsum dolor sit amet' with io.BytesIO() as f: bucket.download_fileobj(KEY, f) print(f.getvalue()) # -> b'Lorem ipsum dolor sit amet'
HTTP版
読み込み
obj = bucket.Object(KEY) print(obj.get()['Body'].read())
get
はdictを返し、その中にBody
というプロパティが存在します。
このBody
はStreamingBodyです。これはReadOnlyのfile object
のようなものです。
書き込み
書き込む場合はバイナリのfile object
かバイナリそのものが利用可能です。
with ORIGIN_PATH.open('rb') as f: obj.put(Body=f) with io.BytesIO(b'Lorem ipsum dolor sit amet') as f: obj.put(Body=f) obj.put(Body=b'Lorem ipsum dolor sit amet')
感想
S3にファイルを置いてPythonで読み書きしたい場合の整理ができたと思います。 オンメモリで取り扱う方法も整理できました。